package Linux五种IO模型;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.*;
import java.nio.charset.Charset;
import java.util.Iterator;
import java.util.Set;

/**
 * @author coderFu
 * @create 2022/3/6
 * NIO 升级  将 accept 和 read操作 交给 子线程来处理
 */
public class NIOServerDemo {

    public static void main(String[] args) throws IOException {

        // ServerSelector负责轮询是否有新的链接
        // 创建一个 选择器
        Selector serverSelector = Selector.open();

        // clientSelector 负责轮询链接是否有可读数据
        Selector clientSelector = Selector.open();

        new Thread(()->{

            try{
                // 对应 IO 编程中的服务端启动
                // 创建服务端 channel
                ServerSocketChannel listenChannel = ServerSocketChannel.open();
                // 绑定监听端口
                listenChannel.bind(new InetSocketAddress(9991));
                // 设置为 非阻塞
                System.out.println("服务端开放的端口为："+listenChannel.getLocalAddress());
                listenChannel.configureBlocking(false);
                // 服务端的channel 注册到服务端的选择器中
                listenChannel.register(serverSelector, SelectionKey.OP_ACCEPT);
                while (true){
                    // 当有新的链接到来
                    if (serverSelector.select(1)>0){
                        // 检测是否有新的链接，这里的1 指的是阻塞的时间为 1ms
                        Set<SelectionKey> selectionKeys = serverSelector.selectedKeys();
                        Iterator<SelectionKey> keyIterator = selectionKeys.iterator();
                        while (keyIterator.hasNext()){
                            SelectionKey key = keyIterator.next();
                            // 当前是否为可连接的
                            if (key.isAcceptable()){
                                try {
                                    ServerSocketChannel serverSocketChannel = (ServerSocketChannel) key.channel();
                                    // 返回客户端的链接 accept 的返回结果为客户端的channel
                                    SocketChannel clientChannel = serverSocketChannel.accept();
                                    //客户端设置为channel 设置为 非阻塞
                                    clientChannel.configureBlocking(false);
                                    // 注册到客户端 的 channel 中
                                    clientChannel.register(clientSelector,SelectionKey.OP_READ);
                                }finally {
                                    // 移除当前的 channel 因为 当下一链接来到的时候 会再次进入这里
                                    keyIterator.remove();
                                }
                            }
                        }
                    }
                }
            } catch (IOException e) {
                e.printStackTrace();
            }


        },"connectDeal").start();


        new Thread(() ->{

            // 负责客户端的读写
            while (true){
                // 判断可读数据
                try {
                    if(clientSelector.select(1 )>0){
                        Set<SelectionKey> selectionKeys = clientSelector.selectedKeys();
                        Iterator<SelectionKey> iterator = selectionKeys.iterator();
                        while (iterator.hasNext()){
                            SelectionKey key = iterator.next();
                            if (key.isReadable()){
                                try {
                                    // 将channel 中的数据读取的 buffer中
                                    SocketChannel channel = (SocketChannel) key.channel();
                                    // 缓冲区的创建， 申请所需的容量大小
                                    ByteBuffer byteBuffer = ByteBuffer.allocate(1024);
                                    // 读进来，写出去
                                    channel.read(byteBuffer);
                                    // 将缓冲区改为读模式
                                    byteBuffer.flip();
                                    // 读取缓冲区中的数据 解码输出
                                    System.out.println(Charset.defaultCharset().newDecoder().decode(byteBuffer));
                                    byteBuffer.clear();

                                }finally {
                                    iterator.remove();
                                    // key 感兴趣的事件为 读事件
                                    key.interestOps(SelectionKey.OP_READ);
                                }
                            }
                        }
                    }
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        },"DataRead").start();








    }
}
